{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基本方法\n",
    "\n",
    "### 如何划分训练集？如何选取验证集？\n",
    "\n",
    "原始数据首先进行数据清洗、特征缩放标准化，归一化等。\n",
    "\n",
    "采用纯随机的采样方式，这种方式对于大量数据集以及对于**目标值分布均匀**的情况是可行的。\n",
    "\n",
    "将训练集数据划分为某一定的比例的训练数据，一部分为验证数据。\n",
    "\n",
    "如果标签分布很不均与，正反例比例很大，则通过分层抽样的方式，每一个抽样的子集都包含一定比例的正负例。\n",
    "\n",
    "1 留出法(hold-out)\n",
    "\n",
    "方法：直接将数据集D划分为两个互斥的集合，其中一个集合作为训练集S，另一个作为测试集T，即.在S上训练出模型后，用T来评估其作为测试误差，作为对泛化误差的估计。\n",
    "\n",
    "2 交叉验证法（crossvalidation）/k折交叉验证（k-fold cross validation）\n",
    "\n",
    "方法：将数据集D划分为k个大小相似的互斥子集，即.每个子集Di都尽可能保持数据分布的一致性，即从D中通过分层采样得到。然后每次都用k-1个子集的并集作为训练集，余下的那个子集作为测试集；这样就可获得k组训练/测试集，从而可进行k次训练和测试，最终返回的是这k个测试结果的均值。\n",
    "\n",
    "3 自助法（bootstrapping）\n",
    "\n",
    "方法：以bootstrap sampling为基础，给定包含m个样本的数据集，我们对它进行采样产生数据集D’：每次随机从D中挑选一个样本，将其拷贝放入D’，然后将该样本放回初始数据集D中，使得该样本在下次采样时仍有可能被采到；这个过程重复m次以后，我们就得到了包含m个样本的数据集D’。将D’用作训练集，D\\D’用作测试集；这样实际评估的模型与期望评估的模型都使用m个训练样本，而仍有数据总量约1/3的、没在训练集中出现的样本用于测试，这样的测试结果，也成为“包外估计”（out-of bag estimate）.\n",
    "\n",
    " 使用条件：\n",
    "\n",
    "（1）在数据集较小，难以有效划分训练/测试集时很有用；\n",
    "\n",
    "（2）可从初始数据集中产生多个不同的训练集，对集成学习等方法很有用。\n",
    "\n",
    " \n",
    "\n",
    "**总结：**自助法产生的数据集改变了初始数据集的分布，这会引入估计偏差。在初始数据量足够时，留出法和交叉验证法更常用一些。\n",
    "\n",
    "在这个过程中，样本在m次采样中始终不被采到的概率是(1-1/m)m，取极限得到\n",
    "\n",
    "即通过自助采样，初始数据集D中约有36.8%的样本未出现在采样数据集D’中。\n",
    "\n",
    "#### 数据集的大小\n",
    "\n",
    "除了数据的分布对于数据集的划分有影响之外，数据集的大小也对于数据集划分也有影响。不同规模的数据集可能训练集、验证集、测试集的划分会有所不同。\n",
    "\n",
    "#### 小规模数据集数据的划分\n",
    "\n",
    "在传统的机器学习中，我们听过70%/30%的划分原则，就是指将整个数据集中的70%用于模型的训练也就是训练集，整个数据集中的30%用于模型的测试也就是测试集，其实这里的测试集被称为验证集或者开发集会更适合。如果想要加入验证集，可能划分标准就改成了60%/20%/20%，也就是指将整个数据集的60%划分为训练集，20%划分为验证集，20%划分为验证集。以上的两种划分标准只适用于小规模的数据集，如果对于上百万、甚至上千万规模的数据集可能需要修改数据集的划分原则。\n",
    "\n",
    "#### 大规模数据集数据的划分\n",
    "\n",
    "我们现在所处的是大数据时代，所以很多数据集的数据规模都是上百万甚至上亿，而且如果想要获取一个好的模型是离不开大数据的。面对这种大规模的数据集时，传统的数据划分原则已经不适用了，验证集和测试集的划分可以低于30%和20%。对于百万级别的数据集，我们可以采用98%/1%/1%的规则来划分数据集。\n",
    "\n",
    "**建议**\n",
    "在有些数据集的划分中，没有真正的测试集，也就是只有训练集和测试集。利用训练集来训练模型，然后通过测试模型在测试集上的表现来调整超参和采用不同的策略来提高模型在测试集上的表现，而没有真正的测试集来评估模型的性能，缺少真正的测试集可能会导致模型过拟合，使用这种方式在测试集上所获取的模型评估是不可靠的。建议不要省略验证集，利用验证集来调整模型，利用测试集来评估模型的指标。如果模型上线的指标要求比较高时，可以适当的加大测试集的数量以此来获取更高精度的评估指标，建议不要超过30%。\n",
    "\n",
    "### 什么是偏差和方差？\n",
    "\n",
    "<img src=\"imgs/biasandvar.jpg\" alt=\"biasandvar\" style=\"zoom: 67%;\" />\n",
    "\n",
    "**准：bias**描述的是根据样本拟合出的模型的输出预测结果的期望与样本真实结果的差距，简单讲，就是在样本上拟合的好不好。要想在bias上表现好，low bias，就得复杂化模型，增加模型的参数，但这样容易过拟合 (overfitting)，过拟合对应上图是high variance，点很分散。low bias对应就是点都打在靶心附近，所以瞄的是准的，但手不一定稳。\n",
    "\n",
    " **确：varience**描述的是样本上训练出来的模型在测试集上的表现，要想在variance上表现好，low varience，就要简化模型，减少模型的参数，但这样容易欠拟合(unfitting)，欠拟合对应上图是high bias，点偏离中心。low variance对应就是点都打的很集中，但不一定是靶心附近，手很稳，但是瞄的不准。\n",
    "\n",
    " 这个靶子上的点(hits)可以理解成一个一个的拟合模型，如果许多个拟合模型都聚集在一堆，位置比较偏，如图中 high bias low variance 这种情景，意味着无论什么样子的数据灌进来，拟合的模型都差不多，这个模型过于简陋了，参数太少了，复杂度太低了，这就是欠拟合；但如果是图中 low bias high variance 这种情景，你看，所有拟合模型都围绕中间那个 correct target 均匀分布，但又不够集中，很散，这就意味着，灌进来的数据一有风吹草动，拟合模型就跟着剧烈变化，这说明这个拟合模型过于复杂了，不具有普适性，就是过拟合。 所以bias和variance的选择是一个tradeoff，过高的variance对应的概念，有点『剑走偏锋』『矫枉过正』的意思，如果说一个人variance比较高，可以理解为，这个人性格比较极端偏执，眼光比较狭窄，没有大局观。而过高的bias对应的概念，有点像『面面俱到』『大巧若拙』的意思，如果说一个人bias比较高，可以理解为，这个人是个好好先生，谁都不得罪，圆滑世故，说话的时候，什么都说了，但又好像什么都没说，眼光比较长远，有大局观。（感觉好分裂 ）\n",
    "\n",
    "### 什么是过拟合？深度学习解决过拟合的方法有哪\n",
    "\n",
    "过拟合（overfitting）是指在模型参数拟合过程中的问题，由于训练数据包含抽样误差，训练时，复杂的模型将抽样误差也考虑在内，将抽样误差也进行了很好的拟合。 具体表现就是最终模型在训练集上效果好；在测试集上效果差。模型泛化能力弱。\n",
    "\n",
    "![](imgs/overfitting.jpg)\n",
    "\n",
    "为什么要解决过拟合现象？这是因为我们拟合的模型一般是用来预测未知的结果（不在训练集内），过拟合虽然在训练集上效果好，但是在实际使用时（测试集）效果差。同时，在很多问题上，我们无法穷尽所有状态，不可能将所有情况都包含在训练集上。所以，必须要解决过拟合问题。 为什么在机器学习中比较常见？这是因为机器学习算法为了满足尽可能复杂的任务，其模型的拟合能力一般远远高于问题复杂度，也就是说，机器学习算法有「拟合出正确规则的前提下，进一步拟合噪声」的能力。\n",
    "\n",
    "#### 解决方法\n",
    "\n",
    "- 数据增强\n",
    "- Early stopping\n",
    "- 增加噪声\n",
    "- 简化网络结构\n",
    "- Dropout\n",
    "- 贝叶斯方法\n",
    "- \n",
    "\n",
    "1 从数据的角度\n",
    "\n",
    "增加标注的数据，增大数据集、 数据增强（加入旋转，平移，缩放，裁剪，加入噪声等等）\n",
    "\n",
    "2 从模型的角度\n",
    "\n",
    "减小模型的复杂度，剪枝，减少层数等。\n",
    "\n",
    "3 加入正则约束\n",
    "\n",
    "让网络的权值处于一定的区间，防止梯度消失，或者Early Stopping\n",
    "\n",
    "4 模型集成\n",
    "\n",
    "训练多个模型，或者使用dropout 让网络训练中在一个模型内进行集成。\n",
    "\n",
    "### 解决欠拟合的方法有哪些？\n",
    "\n",
    "- simpler model structure\n",
    "- regularization\n",
    "- data augmentation\n",
    "- dropout\n",
    "- Bootstrap/Bagging\n",
    "- ensemble\n",
    "- early stopping\n",
    "- utilize invariance\n",
    "- Bayesian\n",
    "\n",
    "### 深度模型参数调整的一般方法论？\n",
    "\n",
    "刚刚开始调试，进行梯度检查，数值梯度和分析梯度进行误差检测。\n",
    "\n",
    "首先进行超参数的初始设定，还有训练参数的吃时候，Pytorch每个layer采用了合适的方法进行初始，一般不需要自己指定，但是理论上说，随机初始化，或者正态分布的初始化normal，不如**xavier**效果好。或者有些网络也适合uniform初始化，所以一个好的初始化，是成功训练的基础。\n",
    "\n",
    "####  超参数范围\n",
    "\n",
    "建议优先在对数尺度上进行超参数搜索。比较典型的是学习率和正则化项，我们可以从诸如0.001 0.01 0.1 1 10，以10为阶数进行尝试。因为他们对训练的影响是相乘的效果。不过有些参数，还是建议在原始尺度上进行搜索，例如dropout值: 0.3 0.5 0.7)。\n",
    "\n",
    "#### 经验参数\n",
    "\n",
    "这里给出一些参数的经验值，避免大家调参的时候，毫无头绪。\n",
    "\n",
    "  > Tesla AI 主管、李飞飞高徒 Andrej Karpathy  : 3e-4 是对 Adam 最好的学习速率，妥妥地~ 只是一个玩笑。。。\n",
    "  \n",
    "  \n",
    "- learning rate: 1 0.1 0.01 0.001, 一般从1开始尝试。很少见learning rate大于10的。学习率一般要随着训练进行衰减。衰减系数一般是0.5。 衰减时机，可以是验证集准确率不再上升时，或固定训练多少个周期以后。\n",
    "  不过更建议使用自适应梯度的办法，例如adam,adadelta,rmsprop等，这些一般使用相关论文提供的默认值即可，可以避免再费劲调节学习率。对RNN来说，有个经验，如果RNN要处理的序列比较长，或者RNN层数比较多，那么learning rate一般小一些比较好，否则有可能出现结果不收敛，甚至Nan等问题。\n",
    "\n",
    "  学习率退火，建立学习率衰减的周期表，这个很常见。\n",
    "  \n",
    "- 网络层数： 先从1层开始。\n",
    "- 每层结点数： 16 32 128，超过1000的情况比较少见。超过1W的从来没有见过。\n",
    "- batch size: 128上下开始。batch size值增加，的确能提高训练速度。但是有可能收敛结果变差。如果显存大小允许，可以考虑从一个比较大的值开始尝试。因为batch size太大，一般不会对结果有太大的影响，而batch size太小的话，结果有可能很差。\n",
    "- clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w1^2+w2^2….),如果value超过了阈值，就算一个衰减系系数,让value的值等于阈值: 5,10,15\n",
    "- dropout： 0.5\n",
    "- L2正则：1.0，超过10的很少见。\n",
    "- 词向量embedding大小：128，256\n",
    "- 正负样本比例： 这个是非常忽视，但是在很多分类问题上，又非常重要的参数。很多人往往习惯使用训练数据中默认的正负类别比例，当训练数据非常不平衡的时候，模型很有可能会偏向数目较大的类别，从而影响最终训练结果。除了尝试训练数据默认的正负类别比例之外，建议对数目较小的样本做过采样，例如进行复制。提高他们的比例，看看效果如何，这个对多分类问题同样适用。\n",
    "  在使用mini-batch方法进行训练的时候，尽量让一个batch内，各类别的比例平衡，这个在图像识别等多分类任务上非常重要。\n",
    "\n",
    "\n",
    "\n",
    "#### 初始设定完超参数后\n",
    "\n",
    "可以画出Epoch-acc 等模型评价指标的曲线，观察曲线是否收敛，也可以从loss,error等角度出发，查看每个epoch是否处于下降的趋势。\n",
    "\n",
    "神经网络的代码，比一般的代码要难调试不少，和编译错误以及运行时程序崩溃相比，神经网络比较棘手的地方，往往在于程序运行正常，但是结果无法收敛，这个检查起来可要麻烦多了。下面是根据我平时调试神经网络的经验，总结的一些比较通用的调试技巧，后续会再写一篇文章，专门介绍一下theano如何进行调试，希望能对大家调试神经网络有所帮助。\n",
    "\n",
    "# 遇到Nan怎么办？\n",
    "\n",
    "Nan问题，我相信大部分人都遇到过，一般可能是下面几个原因造成的：\n",
    "\n",
    "1. 除0问题。这里实际上有两种可能，一种是被除数的值是无穷大，即Nan，另一种就是除数的值是0。之前产生的Nan或者0，有可能会被传递下去，造成后面都是Nan。请先检查一下神经网络中有可能会有除法的地方，例如softmax层，再认真的检查一下数据。我有一次帮别人调试代码，甚至还遇到过，训练数据文件中，有些值就是Nan。。。这样读进来以后，开始训练，只要遇到Nan的数据，后面也就Nan了。可以尝试加一些日志，把神经网络的中间结果输出出来，看看哪一步开始出现Nan。后面会介绍Theano的处理办法。\n",
    "2. 梯度过大，造成更新后的值为Nan。特别是RNN，在序列比较长的时候，很容易出现梯度爆炸的问题。一般有以下几个解决办法。\n",
    "   1. 对梯度做clip(梯度裁剪），限制最大梯度,其实是value = sqrt(w1^2+w2^2….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15。\n",
    "   2. 减少学习率。初始学习率过大，也有可能造成这个问题。需要注意的是，即使使用adam之类的自适应学习率算法进行训练，也有可能遇到学习率过大问题，而这类算法，一般也有一个学习率的超参，可以把这个参数改的小一些。\n",
    "3. 初始参数值过大，也有可能出现Nan问题。输入和输出的值，最好也做一下归一化。具体方法可以参考我之前的一篇文章：[深度学习个人炼丹心得 - 炼丹实验室 - 知乎专栏](http://zhuanlan.zhihu.com/p/20767428)\n",
    "\n",
    "# 神经网络学不出东西怎么办？\n",
    "\n",
    "可能我们并没有遇到，或者解决了Nan等问题，网络一直在正常的训练，但是cost降不下来，预测的时候，结果不正常。\n",
    "\n",
    "1. 请打印出训练集的cost值和测试集上cost值的变化趋势，正常情况应该是训练集的cost值不断下降，最后趋于平缓，或者小范围震荡，测试集的cost值先下降，然后开始震荡或者慢慢上升。如果训练集cost值不下降，有可能是代码有bug，有可能是数据有问题（本身有问题，数据处理有问题等等），有可能是超参（网络大小，层数，学习率等）设置的不合理。\n",
    "   请人工构造10条数据，用神经网络反复训练，看看cost是否下降，如果还不下降，那么可能网络的代码有bug，需要认真检查了。如果cost值下降，在这10条数据上做预测，看看结果是不是符合预期。那么很大可能网络本身是正常的。那么可以试着检查一下超参和数据是不是有问题。\n",
    "2. 如果神经网络代码，全部是自己实现的，那么强烈建议做梯度检查。确保梯度计算没有错误。\n",
    "3. 先从最简单的网络开始实验，不要仅仅看cost值，还要看一看神经网络的预测输出是什么样子，确保能跑出预期结果。例如做语言模型实验的时候，先用一层RNN，如果一层RNN正常，再尝试LSTM，再进一步尝试多层LSTM。\n",
    "4. 如果可能的话，可以输入一条指定数据，然后自己计算出每一步正确的输出结果，再检查一下神经网络每一步的结果，是不是一样的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
